Server-Side Request Forgery
略称: SSRF
サーバーが外部に公開していないサーバーにリクエストを送信してしまう脆弱性のこと
攻撃者が応答が閲覧できる場合、情報漏洩につながる
場合によってはAPIリクエストやコマンド発行を行うことで様々な悪用ができる
例
code:server.py
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.get("/proxy")
def proxy():
text = requests.get(url).text
return jsonify({"response": text})
@app.get("/flag")
def get_flag():
if request.remote_addr != "127.0.0.1":
return "ローカルからしか閲覧できません!"
return "SECRET_FLAG"
if __name__ == "__main__":
app.run(debug=True)
http://server.example.com/でサーバーが動いているとする
/proxy?url=http://hogehoge.example.com/でhttp://hogehoge.example.com/の内容を取得できる
ユーザーが入力したURLを検証せずにそのままリクエストを送信していることに注目する
/flagにアクセスするとフラグが貰えるが、内部(127.0.0.1)からしか閲覧できない これを取得することを考える
/proxy?url=http://server.example.com/flagにアクセスしてみる
/proxyの処理でhttp://server.example.com/flagにリクエストが送信される
内部からリクエストを送っているため、送信元IPは127.0.0.1になる
/flagは127.0.0.1からのリクエストが来たので、フラグを返す /proxyが/flagのレスポンスをそのまま返す
結果的に/flagの内容を外部から取得できた!
対策
そもそもそんな機能作るな
SSRFは非常に複雑なので対策が難しい
ホワイトリストで検証する
a.example.com, b.example.comとしか通信しないことが想定されているなら、a.example.com,b.example.com以外のドメインを指すURLを弾けばいい
文字列処理も自分でせずに標準のパーサーを利用するとよい
ツール
テクニック
Filter bypass
127.0.0.1や0.0.0.0が禁止されていても、IPの表現方法は多数ある
http://localhost/
http://127.1/
n進数
127.0.0.1 http://2130706433/ http://017700000001/ http://0x7F000001/
0.0.0.0 http://0/
ipv6
127.0.0.1 http://[0:0:0:0:0:ffff:127.0.0.1]/
0.0.0.0 http://[::]/
DNS http://localtest.me/ http://127.0.0.1.nip.io/
X.X.X.X.nip.ioを解決するとX.X.X.Xが返ってくる
ライブラリによっては正規化が刺さる
example。com, ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜなど
先頭だけチェックしてる場合
http://example.com@127.0.0.1/ → 127.0.0.1
http://example.com.attacker.domain/
URLパーサの挙動の差異の悪用
Orange TsaiさんがBlackHatで発表した手法
Pythonのurllib2でURLを検証してリクエストはrequestsで送るような場合、ふたつのライブラリのURLの解釈の違いを利用することで検証をbypassできる
現在でも使えるかは未検証
Python以外でも原理的に悪用できる可能性がある
@を使ったURL検証のバイパスはいろんなところで見る
リダイレクトによる制限の回避
攻撃者のサーバーを指定できるとき、304などで好きなURLにリダイレクトさせる
プロトコルの変更にも使える
プロトコル
gopher://
任意のデータを任意の送信先に送信することができる最強のプロトコル
gopher://{ip}:{port}/x{payload}で送信できる
URL Encodeすればバイナリも送れる
最近のバージョンのcurlはで%00が使えなくなった。NULLを含まないバイナリなら送れる
curl (libcurl)くらいしか対応してない
file://
Denoのfetchは対応している
CrewCTF 2023 - safe_proxy
ftp://
FTP Bounce Attack
PORTコマンドを利用してファイルの送信先を任意のIP/portにする攻撃
ほとんどのFTPソフトウェアでは対策されているが、対策されていないnodeftpdがCTFではよく使われるっぽい
DNS Rebinding
DNS応答のTTLを極めて短くすることで、ドメインのIPチェックを回避する攻撃
入力されたドメインのIPがY.Y.Y.Yだったら弾くようなプログラムがあるとき
攻撃者が持つevil.example.comを入力する
チェック時にはevil.example.comをX.X.X.Xとして解決させる
このときにTTLを極めて短くすることで、次の通信時にドメインの再解決を行わせる
実際に使用するときに再解決が起きる
このときはevil.example.comをY.Y.Y.Yとして解決させる
これでチェックをbypassできた
TLSの仕様を利用した攻撃手法
攻撃者のサーバーにhttpsなどのTLS通信ができる場合に使用できる
セッションIDを利用して任意のサーバーに任意の短いデータを送信することができる
ブラウザからのSSRF
CSRFで内部IPにPOST
WebRTC
ターゲット
Redis
通信の暗号化や認証がされていない場合、SSRFでRedisコマンドを発行できる
記事にもあるように、RedisはHTTPによるSSRFの対策をしている
現在でも使えるかは要検証
Memcached
Redisと同様にSSRFのターゲットになる
DevTools API
--remote-debugging-port=<port>をつけて起動したChromeはデバッグ用のportを開く
Pupeteerはこれを利用してChromeを操作している
SSRFでこのportにアクセスすることで色々な悪用ができる
クラウド
AWSなどのクラウドサービスでは内部IPにアクセスすることで機密情報を見れることがある
SSRFを利用してクラウドの機密情報を漏洩させることができる
まとめるの面倒なのでHackTricks見てください
他にも色々
内部にサーバーがある限りSSRFで悪用できる可能性がある
資料